package com.example.thread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

// 对比DeadlockExample04来看的话，DeadlockExample05没有使用ReentrantLock，而是使用自定义的NonReentrantLock，而我们把它定义成一个不可重入的。
// 从实际试验的效果来看，发觉，它确实是形成了死锁。这就说明，我们自定义的这个不可重入锁，成功了。
public class DeadlockExample05 {
    // ReentrantLock也是可重入锁
    private static final Lock lock = new NonReentrantLock();
    private static int i = 1;

    public static void main(String[] args) {
        new Thread(() -> {
            recursiveMethod();
        }).start();
    }

    // 递归方法
    private static void recursiveMethod() {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "获取了锁");

        while (DeadlockExample05.i < 10) {
            DeadlockExample05.i++;
            // 递归调用自身
            recursiveMethod();
        }
    }
}

class NonReentrantLock implements Lock {
    private boolean isLocked = false;

    // @Override注解可加可不加，但是建议加上。这个在后记中有详细说明。
    //@Override
    public synchronized void lock() {
        // 最开始isLocked为false，所以不进while，不走wait。
        while (isLocked) {
            try {
                // Lock接口里面里面自带的吧。后来发现是Object方法里面自带的。
                // 关于wait方法的解释，请看后记。
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 改变状态字段。后续再使用lock，都进while，然后走wait方法。
        isLocked = true;
    }

    //@Override
    public synchronized void unlock() {
        // 将锁的状态改为未锁
        isLocked = false;
        // notify和notifyAll方法的区别请看后记
        notify();
    }

    //@Override
    public void lockInterruptibly() throws InterruptedException {
    }

    //@Override
    public boolean tryLock() {
        return false;
    }

    //@Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    //@Override
    public Condition newCondition() {
        return null;
    }
}


// 后记：
// 一点前置知识：java中所有的类，都继承了Object类
// 在 Java 中，几乎所有的类都隐式地直接或间接继承自 Object 类。Object 类是 Java 中所有类的根类，它定义了一些通用的方法，比如 toString()、equals()、hashCode() 等等。如果一个类没有显式地指定它的父类，那么它就隐式地继承自 Object 类。
// 这意味着，即使你没有明确地在类的声明中指定继承自其他类，这个类仍然会隐式地继承自 Object 类。例如：

// 为什么要自己定义不可重入锁
// Java标准库中没有提供现成的不可重入锁实现，因为通常可重入锁更为常见和灵活，而不可重入锁在实际应用中较少见。如果需要使用不可重入锁，可以自己定义一个简单的不可重入锁类。下面是一个简单的不可重入锁的示例实现：

// wait方法是什么？
// wait() 方法是 Object 类中定义的一个方法，它的作用是使当前线程进入等待状态，并释放对象的锁。在调用 wait() 方法后，当前线程将被挂起，直到其他线程调用相同对象上的 notify() 或 notifyAll() 方法，或者指定的等待时间到期，当前线程被唤醒。
// wait() 方法通常与 notify() 和 notifyAll() 方法一起使用，用于实现线程之间的协作，允许线程等待某些条件的发生或者特定事件的触发。
// 在调用 wait() 方法之前，当前线程必须先获得对象的锁，否则会抛出 IllegalMonitorStateException 异常。通常 wait() 方法被放置在一个 synchronized 块中，以确保线程在等待时对象的一致性和状态不受破坏。

// notify和notifyAll方法的区别是啥？
// 注意哦，notify和notifyAll也都是Object类里面的方法哦。由于任何类都继承了Object类，所以，在java中
// notify() 方法用于唤醒等待在该对象上的一个线程。如果有多个线程等待在该对象上，只会唤醒其中一个线程，但是不确定会唤醒哪一个。具体来说，是由系统的线程调度器来决定唤醒哪一个线程。
// notifyAll() 方法用于唤醒等待在该对象上的所有线程。即使有多个线程等待在该对象上，也会唤醒它们所有。这样，所有等待的线程都有机会去竞争对象的锁。

//Override注解可加可不加，都不会报错，并且程序也能正常跑，为什么？
// 在接口实现类中实现接口的抽象方法时，使用 `@Override` 注解是可选的，而不是必需的。`@Override` 注解用于告诉编译器，当前方法是对父类或接口中的方法的重写，这样编译器可以在编译时进行检查，确保方法的签名正确且没有拼写错误。
// 在实现接口时，编译器会强制要求实现接口中的所有抽象方法，因此不使用 `@Override` 注解也不会导致编译错误。然而，使用 `@Override` 注解可以帮助提高代码的可读性和可维护性，让其他开发者清楚地知道这个方法是对接口方法的实现。
// 因此，尽管不使用 `@Override` 注解不会导致错误，但推荐在实现接口的抽象方法时使用 `@Override` 注解，这样可以让代码更加清晰明了。

// 同步块那种上锁方式是可重入的。而ReentrantLock这种锁也是实现的是可重入的。那一个线程多次获取锁后，它解锁的原理是啥？
// 是内部维护了一个计数器。lock一次就计数器递增1，然后unLock一次计数器递减1，直到递减为0的那次unlock，才是真正的解锁。
